﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using Chefbook.DataAccessLayer;
//using Chefbook.DataObjects;

namespace UnitTests.DataObjectsTests
{
    [TestFixture]
    public class CategoryFixture
    {
        private Category root;
        private Category testCategory;
        
        [SetUp]
        public void Init()
        {
            root = new Category();
            root.Name = "Root";
            testCategory = new Category();
            testCategory.Parent = root;
        }

        [Test]
        public void ConstructorTest()
        {
            Assert.AreEqual(root, testCategory.Parent, "Constructor Parent test failed");
            Assert.AreEqual(1, testCategory.Parent.Subcategories.Count((c) => c == testCategory), "Parent's Subs doesn't contain category exactly once");
            Assert.AreEqual("", testCategory.Name, "Constructor Name test failed");
            Assert.AreEqual(0, testCategory.Subcategories.Count, "Constructor SubCategories test failed");
            Assert.AreEqual(0, testCategory.DirectRecipes.Count, "Constructor Recipes test failed");
        }

        [Test]
        public void RenameTest()
        {
            testCategory.Name = "Tasty";
            Assert.AreEqual("Tasty", testCategory.Name, "Rename test failed");
        }

        [Test]
        public void RenameNullTest()
        {
            Assert.Throws<System.Data.ConstraintException>(() => testCategory.Name = null);
        }

        [Test]
        public void DeleteTest()
        {
            var parent = testCategory.Parent;
            new Recipe();
            new Recipe();
            
            testCategory.Delete();
            Assert.IsFalse(parent.Subcategories.Contains(testCategory),"Delete test failed");
            foreach (var recipe in testCategory.DirectRecipes)
            { 
                Assert.IsFalse(recipe.Categories.Contains(testCategory), "Category not removed from all recipes"); 
            }
        }

        [Test]
        public void DeleteNullParentTest()
        {
            testCategory = new Category();
            new Recipe();
            new Recipe();
            Assert.DoesNotThrow(testCategory.Delete);
            foreach (var recipe in testCategory.DirectRecipes)
            {
                Assert.IsFalse(recipe.Categories.Contains(testCategory), "Category not removed from all recipes");
            }
        }

        [Test]
        public void ChangeParentTest()
        {
            root.Name = "Root";
            testCategory.Name = "Test";
            var oldParent = testCategory.Parent;
            var newParent = new Category();
            newParent.Name = "NewParent";
            testCategory.Parent = newParent;
            Assert.IsFalse(oldParent.Subcategories.Contains(testCategory),string.Format("{0} not removed from old parent ({1})",testCategory.Name, oldParent.Name));
            Assert.AreEqual(newParent, testCategory.Parent, "New Parent not set");
            Assert.IsTrue(newParent.Subcategories.Contains(testCategory), "Not added to new parent");
        }

        [Test]
        public void GetAllRecipesTest()
        {
            var recipe = new Recipe();
            testCategory.AddRecipe(recipe);

            var subCategory = new Category();
            var nestedrecipe = new Recipe();
            subCategory.AddRecipe(nestedrecipe);
            testCategory.AddSubcategory(subCategory);

            var subSubCategory = new Category();
            var nestedrecipe2 = new Recipe();
            subSubCategory.AddRecipe(nestedrecipe2);
            subCategory.AddSubcategory(subSubCategory);

            Assert.Contains(subCategory, testCategory.Subcategories);
            Assert.Contains(subSubCategory, subCategory.Subcategories);

            Assert.Contains(recipe, testCategory.AllRecipes.ToList(), "AllRecipes didn't contain direct recipe");
            Assert.Contains(nestedrecipe, testCategory.AllRecipes.ToList(), "AllRecipes didn't contain nested recipe");
        }

        [Test]
        public void GetDirectRecipesTest()
        {
            var recipe = new Recipe();
            testCategory.AddRecipe(recipe);
            
            var subCategory = new Category();
            var nestedrecipe = new Recipe();
            subCategory.AddRecipe(nestedrecipe);
            testCategory.AddSubcategory(subCategory);

            var subSubCategory = new Category();
            var nestedrecipe2 = new Recipe();
            subSubCategory.AddRecipe(nestedrecipe2);
            subCategory.AddSubcategory(subSubCategory);

            Assert.Contains(subCategory, testCategory.Subcategories);
            Assert.Contains(subSubCategory, subCategory.Subcategories);
            Assert.Contains(recipe, testCategory.DirectRecipes, "Recipes didn't contain direct recipe");
            Assert.IsFalse(testCategory.DirectRecipes.Contains(nestedrecipe2), "Recipes contained nested recipe");            
        }

        [Test]
        public void AddRecipeTest()
        {
            var recipe = new Recipe();
            testCategory.AddRecipe(recipe);
            Assert.Contains(recipe, testCategory.DirectRecipes, "Recipe wasn't in Recipes of the category");
            Assert.Contains(testCategory, recipe.Categories.ToList(), "Category wasn't in recipe's list of categories");
        }

        [Test]
        public void AddRecipeTwiceTest()
        {
            var recipe = new Recipe();
            testCategory.AddRecipe(recipe);
            testCategory.AddRecipe(recipe);
            Assert.AreEqual(1, testCategory.DirectRecipes.Count((r) => r == recipe), "Recipe was added more than once");
            Assert.AreEqual(1, recipe.Categories.Count((c) => c == testCategory), "Category was aded to recipe more than once");
        }

        [Test]
        public void AddRecipeNullTest()
        {
            Assert.Throws<ArgumentNullException>(() => testCategory.AddRecipe(null));
        }

        [Test]
        public void RemoveRecipeTest()
        {
            var recipe = new Recipe();
            testCategory.AddRecipe(recipe);
            testCategory.RemoveRecipe(recipe);
            Assert.False(testCategory.DirectRecipes.Contains(recipe), "Recipe wasn't removed from Recipes of the category");
            Assert.False(recipe.Categories.Contains(testCategory), "Category wasn't removed from recipe's list of categories");
        }

        [Test]
        public void RemoveRecipeNullTest()
        {
            Assert.Throws<ArgumentNullException>(() => testCategory.RemoveRecipe(null));
        }

        [Test]
        public void RemoveNotFoundRecipeTest()
        {
            var recipe = new Recipe();
            Assert.Throws<ArgumentException>(() => testCategory.RemoveRecipe(recipe));
        }
    }
}
